package com.xiashitech.agent.rule.usermatch.inst;

import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.auto.service.AutoService;
import com.xiashitech.agent.config.StartConfig;
import com.xiashitech.agent.dto.UserRequest;
import com.xiashitech.agent.instrumentation.base.context.TraceContextVars;
import com.xiashitech.agent.instrumentation.utils.BaggageUtil;
import com.xiashitech.agent.rule.usermatch.base.UserInfoMatchRule;
import com.xiashitech.agent.utils.JsonUtil;
import com.xiashitech.interfaces.agent.dto.AgentConfigDTO;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.ServletWebRequest;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.nio.file.OpenOption;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * 只针对OAuth 2.0受保护资源，所以获取Bearer模式的秘钥，其它模式不支持，例如：Basic、Digest、HOBA、Mutual、Negotiate/NTLM、VAPID、SCRAM、AWS4-HMAC-SHA256
 * @param <REQUEST>
 */
//@AutoService(UserInfoMatchRule.class)
public class XSCommonHS256JWTUserMatch<REQUEST> implements UserInfoMatchRule<REQUEST>{
    @Override
    public UserRequest extractUserInfo(REQUEST request) {
        UserRequest userRequest = UserRequest.builder().requestDate(String.valueOf(new Date().getTime())).build();
        String jwtToken = "";
        try {
            if(!request.getClass().getName().equals("javax.servlet.http.HttpServletRequest") && !request.getClass().getName().equals("org.springframework.web.context.request.ServletWebRequest") && !"org.apache.catalina.connector.RequestFacade".equals(request.getClass().getName())) {
                return UserRequest.builder().build();
            }
            Object jwtTokenObj = request.getClass().getMethod("getHeader",new Class[]{String.class}).invoke(request,"Authorization");
            if(jwtTokenObj != null) {
                jwtToken = jwtTokenObj.toString();
            }
            jwtToken = extractJwt(jwtToken);

            if (jwtTokenObj==null || "".equals(jwtTokenObj))
            {
                int i=0;
                Cookie[] cookies = null;

                if("org.apache.catalina.connector.RequestFacade".equals(request.getClass().getName())) // instanceof RequestFacade)
                {
                    // cookies = ((RequestFacade)request).getCookies();
                    cookies = (Cookie[]) request.getClass().getMethod("getCookies").invoke(request);
                }
                else if(request instanceof ServletWebRequest){
                    cookies = ((ServletWebRequest)request).getRequest().getCookies();
                }
                String systemId = StartConfig.getParamValueFromKey(StartConfig.systemId);
                AgentConfigDTO config = AgentConfigDTO.getConfig();
                Map<String, String> cookieKey = config.getJwtCookieKey(); // 服务端没配置时，下一步会报空指针
                if(cookieKey != null && cookies != null) {
                    String jwtCookieKey = cookieKey.get(systemId);
                    for (Cookie item : cookies) {
                        if (Optional.ofNullable(jwtCookieKey).isPresent()) {
                            if (item.getName().equals(jwtCookieKey)) {
                                jwtToken = item.getValue();
                            }

                        } else if (item.getName().contains("jwt") && i == 0) {
                            i++;
                            jwtToken = item.getValue();
                        }
                        if (jwtToken.startsWith("Bearer")) {
                            jwtToken = extractJwt(jwtToken);
                        }
                    }
                }
            }
            if(jwtToken != null && !"".equals(jwtToken)) {
                final List<String> tokens = StrUtil.split(jwtToken, CharUtil.DOT);
                if (3 != tokens.size()) {
                    return UserRequest.builder().build();
                }
                JWT jwt = JWTUtil.parseToken(jwtToken);
                //boolean verifyKey = jwt.setKey(AgentConfigDTO.getConfig().getSystemJwtKey().get(StartConfig.getParamValueFromKey(StartConfig.systemId)).getBytes()).verify();
                //boolean verifyTime = jwt.validate(0);
                //Boolean verifyKey = true;
                //if(verifyKey) {
                List<String> usernamekeys = AgentConfigDTO.getConfig().getUsernamekey();
                String usernamekey = AgentConfigDTO.getConfig().getJwtUserKey().get(StartConfig.getParamValueFromKey(StartConfig.systemId));
                if(usernamekey != null) {
                    //usernamekeys.forEach(userkey -> {
                    Object userInfo = jwt.getPayload(usernamekey);
                    if(userInfo!=null) {
                        String userStr = userInfo.toString();
                        if (!JsonUtil.isJson((userStr))) {
                            userRequest.setUserid(userStr);
                        } else {
                            ObjectNode objectNode = JsonUtil.getJsonNode(userInfo);
                            List<String> userids = AgentConfigDTO.getConfig().getUserid();
                            userids.forEach(userid -> {
                                JsonNode jsonNode = objectNode.findValue(userid);
                                if (jsonNode != null) {
                                    if (jsonNode.isValueNode()) {
                                        userRequest.setUserid(jsonNode.asText());
                                    }
                                }
                            });
                        }
                    }
                    //});
                }
                // }
            }
        } catch (Exception e) {
            // StartConfig.throwableStackPrinter(e);
            e.printStackTrace();
        }
        if(userRequest.getUserid() != null && !"".equals(userRequest.getUserid())) {
            TraceContextVars.get().set("xs.userId",userRequest.getUserid());
        }
        return userRequest;
    }

    private String extractJwt(String jwt) {
        String processJwt = "";
        if(jwt != null && !"".equals(jwt) && jwt.startsWith("Bearer")) {
            processJwt = jwt.substring(7);
        }
        return processJwt;
    }

    @Override
    public Integer getOrder() {
        return 2;
    }
}
